package org.rcgwt.client.ui.grid.renderer;

import org.rcgwt.client.ui.grid.internal.ViewRenderContextImpl;
import org.rcgwt.client.ui.grid.internal.model.IElementModel;
import org.rcgwt.client.ui.grid.internal.model.IGridModel;
import org.rcgwt.client.ui.grid.internal.model.IElementModelDelta;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModelElements;
import org.rcgwt.client.ui.grid.model.IColumn;
import org.rcgwt.client.ui.grid.model.IGridWidget;
import org.rcgwt.client.ui.grid.provider.IColumnProvider;
import org.rcgwt.client.ui.grid.theme.CssStyle;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;

/**
 * 
 * @author Olivier Oeuillot (latest modification by $Author: $)
 * @version $Revision: $ $Date: $
 */
public class DefaultGridRenderer implements IGridRenderer {

    @Override
    public IGridRenderContext createRenderContext(IGridModel gridModel) {
        return new ViewRenderContextImpl(gridModel);
    }

    @Override
    public Element renderRoot(IGridRenderContext gridRenderContext,
            Document document) {

        return document.createDivElement();
    }

    public void postRenderRoot(IGridRenderContext gridRenderContext,
            Element gridElement) {

        IGridModel gridModel = gridRenderContext.getGridModel();

        IGridWidget grid = gridModel.getGridWidget();

        IGridTheme gridTheme = gridRenderContext.getTheme();

        updateStateMask(gridRenderContext, grid.getElement(), gridModel);

        Style rootStyle = gridElement.getStyle();

        int gridBorderWidth = gridTheme.getGridBorderWidth();

        rootStyle.setPropertyPx(Styles.BORDER_WIDTH, gridBorderWidth);

        rootStyle.setPropertyPx(Styles.WIDTH, grid.getWidth());
        rootStyle.setPropertyPx(Styles.HEIGHT, grid.getHeight());
    }

    @Override
    public void render(IGridRenderContext gridRenderContext, Element gridElement) {

        IGridTheme theme = gridRenderContext.getTheme();

        IGridModel gridModel = gridRenderContext.getGridModel();

        int viewModelIndex = 0;
        for (IViewModel viewModel : gridModel.listViewModels()) {
            IViewRenderContext viewRenderContext = gridRenderContext
                    .getViewRenderContext(viewModel);

            IViewRenderer viewRenderer = theme.getViewRenderer(viewModel);

            Element viewElement = viewRenderer.render(viewRenderContext,
                    gridElement, viewModel, viewModelIndex++);

            gridElement.appendChild(viewElement);
        }

        updateLayout(gridRenderContext);
    }

    @Override
    public void update(IGridRenderContext gridRenderContext, Element gridElement) {

        IGridTheme theme = gridRenderContext.getTheme();

        IGridModel gridModel = gridRenderContext.getGridModel();

        int viewModelIndex = 0;
        for (IViewModel viewModel : gridModel.listViewModels()) {

            IViewRenderContext viewRenderContext = gridRenderContext
                    .getViewRenderContext(viewModel);

            IViewRenderer viewRenderer = theme.getViewRenderer(viewModel);

            Element viewElement = viewModel.getViewModelElements()
                    .getViewElement();

            viewRenderer.update(viewRenderContext, viewElement, viewModel,
                    viewModelIndex++);
        }
    }

    public void updateLayout(IGridRenderContext gridRenderContext) {

        IGridModel gridModel = gridRenderContext.getGridModel();
        IGridWidget grid = gridModel.getGridWidget();
        IGridTheme gridTheme = gridRenderContext.getTheme();

        int viewBorderWidth = gridTheme.getViewBorderWidth();

        int gridPaddings[] = gridTheme.getGridPaddings();

        int totalViewWidth = grid.getWidth() - gridPaddings[3]
                - gridPaddings[1] - gridTheme.getGridBorderWidth() * 2;

        int totalViewHeight = grid.getHeight() - gridPaddings[0]
                - gridPaddings[2] - gridTheme.getGridBorderWidth() * 2;

        int innerViewWidth = totalViewWidth - gridTheme.getViewBorderWidth()
                * 2;
        int innerViewHeight = totalViewHeight - gridTheme.getViewBorderWidth()
                * 2;

        int totalColumnsWidth = innerViewWidth;

        IColumnProvider columnProvider = grid.getColumnProvider();

        int count = columnProvider.getColumnCount();

        int columnSizes[] = new int[count];
        int pws[] = new int[count];
        int pcSize = 0;
        int totalColumnSize = 0;

        int columnModelIndexes[] = gridRenderContext.getColumnModelIndexes();
        for (int i = 0; i < columnModelIndexes.length; i++) {
            int columnModelIndex = columnModelIndexes[i];

            IColumn column = columnProvider
                    .getColumnByModelIndex(columnModelIndex);

            int w = column.getWidth();
            if (w < 0) {
                if (totalColumnsWidth <= 0) {
                    continue;
                }

                int pw = column.getPercentWidth();
                if (pw > 0) {
                    pcSize += pw;
                    pws[i] = pw;
                }

                continue;
            }

            totalColumnSize += w;
            columnSizes[i] = w;
        }

        if (pcSize > 0) {
            int pw = totalColumnsWidth - totalColumnSize;
            if (pw > 0) {
                double d = pw / (double) pcSize;

                for (int i = 0; i < pws.length; i++) {
                    if (pws[i] <= 0) {
                        continue;
                    }

                    int w = (int) (pws[i] * d);
                    columnSizes[i] = w;
                    totalColumnSize += w;
                }
            }
        }

        int sx = gridPaddings[3];
        int sy = gridPaddings[0];

        for (IViewModel viewModel : gridModel.listViewModels()) {
            IViewModelElements viewModelElements = viewModel
                    .getViewModelElements();

            IViewRenderContext viewRenderContext = gridRenderContext
                    .getViewRenderContext(viewModel);

            Element viewElement = viewModelElements.getViewElement();

            Style containerStyle = viewElement.getStyle();
            containerStyle.setPropertyPx(Styles.LEFT, sx);
            containerStyle.setPropertyPx(Styles.TOP, sy);
            containerStyle.setPropertyPx(Styles.WIDTH, totalViewWidth);
            containerStyle.setPropertyPx(Styles.HEIGHT, totalViewHeight);
            containerStyle.setPropertyPx(Styles.BORDER_WIDTH, viewBorderWidth);

            IViewRenderer viewRenderer = gridTheme.getViewRenderer(viewModel);

            viewRenderer.updateLayout(viewRenderContext, viewModel,
                    viewElement, innerViewWidth, innerViewHeight,
                    totalColumnSize, columnSizes);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void processDelta(IGridRenderContext gridRenderContext,
            Element gridElement,
            IElementModelDelta<IGridModel> modelElementDelta) {

        IGridTheme gridTheme = gridRenderContext.getTheme();

        if (modelElementDelta.hasStateMaskChanged()) {
            updateStateMask(gridRenderContext, gridElement, gridRenderContext
                    .getGridModel());
        }

        IElementModelDelta< ? extends IElementModel> deltas[] = modelElementDelta
                .listChildrenDeltas();

        IViewModel viewModels[] = gridRenderContext.getGridModel()
                .listViewModels();

        for (IElementModelDelta< ? extends IElementModel> delta : deltas) {
            if (delta == null) {
                continue;
            }

            IElementModel elementModel = delta.getModelElement();
            if (elementModel.getType() == IElementModel.Type.VIEW) {

                // Evenement sur une vue

                IViewModel viewModel = (IViewModel) elementModel;
                IElementModelDelta<IViewModel> viewElementDelta = (IElementModelDelta<IViewModel>) delta;

                IViewRenderContext viewRenderContext = gridRenderContext
                        .getViewRenderContext(viewModel);

                IViewRenderer viewRenderer = gridTheme
                        .getViewRenderer(viewModel);

                Element viewElement = viewModel.getViewModelElements()
                        .getViewElement();

                viewRenderer.processDelta(viewRenderContext, viewElement,
                        viewElementDelta);
                continue;
            }

            // Un autre objet que la vue, que l'on itere sur une vue !

            for (IViewModel viewModel : viewModels) {

                IViewRenderContext viewRenderContext = gridRenderContext
                        .getViewRenderContext(viewModel);

                AbstractRenderer.processDelta(viewRenderContext, delta);
            }

        }
    }

    protected void updateStateMask(IGridRenderContext gridRenderContext,
            Element element, IGridModel gridModel) {

        IGridWidget grid = gridModel.getGridWidget();

        IGridTheme gridTheme = gridRenderContext.getTheme();

        grid.setClassName(gridTheme.getGridCssStyle(gridModel).getClassName());

        CssStyle themeCssStyle = getCssStyle(gridRenderContext, gridModel);
        if (themeCssStyle != null) {
            grid.addStyleName(themeCssStyle.getClassName());
        }
    }

    protected CssStyle getCssStyle(IGridRenderContext gridRenderContext,
            IGridModel gridModel) {

        IGridTheme gridTheme = gridRenderContext.getTheme();

        return gridTheme.getGridThemeCssStyle(gridModel);
    }

}
